package com.stephenone.zhdaily.data.source;

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;

import com.google.common.base.Optional;
import com.stephenone.zhdaily.data.bean.News;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Nonnull;

import io.reactivex.Flowable;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.stephenone.zhdaily.data.source.local.NewsLocalDataSource.PRARM_LASTEST;

/**
 * Created by Administrator on 2018/3/5.
 */

public class NewsRepository implements NewsDataSource {
    private static final String TAG = NewsRepository.class.getSimpleName();
    @Nullable
    private static NewsRepository INSTANCE = null;

    @NonNull
    private final NewsDataSource mNewsRemoteDataSource;

    @NonNull
    private final NewsDataSource mNewsLocalDataSource;

    @Nullable
    Map<String,News> mCachedNews;

    boolean mCacheIsDirty = false;

    private NewsRepository(@NonNull NewsDataSource newsRemoteDataSource,
                           @NonNull NewsDataSource newsLocalDataSource){
        mNewsRemoteDataSource = checkNotNull(newsRemoteDataSource);
        mNewsLocalDataSource = checkNotNull(newsLocalDataSource);
    }

    public static NewsRepository getInstance(@NonNull NewsDataSource newsRemoteDataSource,
                                             @NonNull NewsDataSource newsLocalDataSource){
        if (INSTANCE == null) {
            INSTANCE = new NewsRepository(newsRemoteDataSource, newsLocalDataSource);
        }
        return INSTANCE;
    }

    @Override
    public Flowable<List<News>> getNews() {
        Log.e(TAG, "getNews: ");
        News cachedNews = getNewsWithDate(PRARM_LASTEST);
        if (cachedNews != null && !mCacheIsDirty) {
            return Flowable.fromIterable(mCachedNews.values()).toList().toFlowable();
        }else if (mCachedNews == null) {
            mCachedNews = new HashMap<>();
        }

        //如果内存没有，那么只返回latest的数据
        Flowable<Optional<News>> localNews = getNewsWithDateFromLocalRepository(PRARM_LASTEST);
        Flowable<Optional<News>> remoteNews = getAndSaveRemoteLatestNews();

        Log.e(TAG, "getNews: mCacheIsDirty" + mCacheIsDirty);
        if (mCacheIsDirty) {
            return remoteNews.map(newsOptional -> {
                List list =  new ArrayList<>();
                list.add(newsOptional.get());
                return list;
            });
        } else {
            return Flowable.concat(localNews,remoteNews)//请求多个数据源
                    .filter(newsOptional -> newsOptional.isPresent())//过滤掉空的
                    .firstElement()//哪层有数据就用哪层的，之后就不走后面的逻辑了
                    .toFlowable()
                    .map(newsOptional -> {
                        List list =  new ArrayList<>();
                        list.add(newsOptional.get());
                        return list;
                    });
        }
    }

    @Override
    public Flowable<Optional<News>> getLatestNews() {
        Log.e(TAG, "getLatestNews: ");
        News cachedNews = getNewsWithDate(PRARM_LASTEST);
        if (cachedNews != null && !mCacheIsDirty) {
            return Flowable.just(Optional.of(cachedNews));
        }

        if (mCachedNews == null) {
            mCachedNews = new HashMap<>();
        }

        Flowable<Optional<News>> localNews = getNewsWithDateFromLocalRepository(PRARM_LASTEST);
        Flowable<Optional<News>> remoteNews = getAndSaveRemoteLatestNews();
        return Flowable.concat(localNews,remoteNews)//请求多个数据源
                .filter(newsOptional -> newsOptional.isPresent())//过滤掉空的
                .firstElement()//哪层有数据就用哪层的，之后就不走后面的逻辑了
                .toFlowable();
    }

    @Override
    public Flowable<Optional<News>> getOneNews(@NonNull String date) {
        News cachedNews = getNewsWithDate(date);
        if (cachedNews != null) {
            return Flowable.just(Optional.of(cachedNews));
        }

        if (mCachedNews == null) {
            mCachedNews = new HashMap<>();
        }

        Flowable<Optional<News>> localNews = getNewsWithDateFromLocalRepository(date);
        Flowable<Optional<News>> remoteNews = getAndSaveRemoteNews(date);
        return Flowable.concat(localNews,remoteNews)
                .filter(newsOptional -> newsOptional.isPresent())//过滤掉空的
                .firstElement()
                .toFlowable();
    }

    private Flowable<Optional<News>> getAndSaveRemoteNews(@NonNull String date) {
        return mNewsRemoteDataSource
                .getOneNews(date)
                .doOnNext(newsOptional -> {
                    Log.e(TAG, "getAndSaveRemoteNews: " +  date);
                    if (newsOptional.isPresent()) {
                        News news = newsOptional.get();
                        mNewsLocalDataSource.saveNews(news.getDate(),news);
                        mCachedNews.put(news.getDate(),news);
                    }
                }).doOnComplete(() -> {
                    mCacheIsDirty = false;
                    Log.e(TAG, "getAndSaveRemoteNews: mCacheIsDirty" +  mCacheIsDirty);
                });
    }

    private Flowable<Optional<News>> getAndSaveRemoteLatestNews() {
        return mNewsRemoteDataSource
                .getLatestNews()
                .doOnNext(newsOptional -> {
                    Log.e(TAG, "getAndSaveRemoteLatestNews: ");
                    if (newsOptional.isPresent()) {
                        News news = newsOptional.get();
                        mNewsLocalDataSource.saveNews(PRARM_LASTEST,news);
                        mCachedNews.put(PRARM_LASTEST,news);
                    }
                }).doOnComplete(() -> {
                    mCacheIsDirty = false;
                    Log.e(TAG, "getAndSaveRemoteLatestNews: mCacheIsDirty" +  mCacheIsDirty);
                });
    }

    private Flowable<Optional<News>> getNewsWithDateFromLocalRepository(String date) {
        Log.e(TAG, "getNewsWithDateFromLocalRepository: " );
        return mNewsLocalDataSource
                .getOneNews(date)
                .doOnNext(newsOptional -> {
                    if (newsOptional.isPresent()) {
                        mCachedNews.put(date,newsOptional.get());
                    }
                })
                .firstElement().toFlowable();
    }

    private News getNewsWithDate(String date) {
        checkNotNull(date);
        if (mCachedNews == null || mCachedNews.isEmpty()) {
            return null;
        }else {
            return mCachedNews.get(date);
        }
    }



    @Override
    public void refreshNews() {
        mCacheIsDirty = true;
    }

    @Override
    public void saveNews(@NonNull String date,@Nonnull News news) {

    }

    public static void destroyInstance() {
        INSTANCE = null;
    }

}
